<template>
  <div id="taskRecordManagePage" style="margin-left: 15px">
    <!-- 面包屑导航区域 -->
    <el-breadcrumb
        separator-class="el-icon-arrow-right"
        style="padding: 12px 12px"
    >
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>任务记录管理</el-breadcrumb-item>
      <el-breadcrumb-item>任务记录列表</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 卡片视图区域 -->
    <el-card
        shadow="hover"
        style="margin-bottom: 15px; min-height: 580px; margin-right: 15px"
    >
      <el-row :gutter="20" style="margin: 12px 0">
        <el-col :span="3">
          <el-select v-model="queryInfo.taskState" class="m-2" placeholder="Select" size="small">
            <el-option label="选择任务状态" value=""></el-option>
            <el-option
                v-for="item in taskStates"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            />
          </el-select>
        </el-col>
        <el-col :span="8">
          <!-- 搜索与添加区域 -->
          <el-input
              v-model="queryInfo.keyword"
              placeholder="请输入内容搜索"
              :prefix-icon="Search"
              class="w-50 m-2"
          >
            <template #append>
              <el-button :icon="Search" @click="handleSearch" />
            </template>
          </el-input>
        </el-col>
      </el-row>
      <!-- 任务记录列表区域  -->
      <el-table
          :data="taskRecordList"
          v-loading="queryLoading"
          ref="taskRecordTable"
          border
          stripe
          table-layout="auto"
          @expand-change="exapndRow"
      >
        <el-table-column label="序号" type="index"></el-table-column>
        <el-table-column
            label="任务名称"
            prop="taskInfo.name"
        ></el-table-column>
        <el-table-column
            label="任务状态"
            prop="taskStateName"
        ></el-table-column>
        <el-table-column label="启动时间" prop="launchTime"></el-table-column>
        <el-table-column label="停止时间" prop="endTime"></el-table-column>
        <el-table-column
            label="调度日志"
            prop="errorLog"
            show-overflow-tooltip
        ></el-table-column>
        <el-table-column label="监控" type="expand">
          <template #default>
            <!-- 此处放置监控代码 -->
            <el-progress
                :text-inside="true"
                :stroke-width="26"
                style="margin: 4px 0 12px 12px"
                :percentage="progressOfData"
            >
              <span>运行进度 {{ progressOfData.toFixed(2) }} %</span>
            </el-progress>
            <div id="monitorContainer">
              <line-graph
                  :graphId="'readRowSpeedGraph'"
                  :stats="readRowsPerSecond"
                  :title="'读取速度(行/s)'"
              />
              <line-graph
                  :graphId="'writeRowSpeedGraph'"
                  :stats="writeRowsPerSecond"
                  :title="'写入速度(行/s)'"
              />
              <line-graph
                  :graphId="'readByteSpeedGraph'"
                  :stats="readBytesPerSecond"
                  :title="'读取速度(MB/s)'"
              />
              <line-graph
                  :graphId="'writeByteSpeedGraph'"
                  :stats="writeBytesPerSecond"
                  :title="'写入速度(MB/s)'"
              />
              <line-graph
                  :graphId="'readBlockTimeGraph'"
                  :stats="readBufferBlockingTimePer10Kr"
                  :title="'每万条记录读缓冲阻塞时间(ms)'"
              />
              <line-graph
                  :graphId="'writeBlockTimeGraph'"
                  :stats="writeBufferBlockingTimePer10Kr"
                  :title="'每万条记录写缓冲阻塞时间(ms)'"
              />
              <pie-graph
                  :graphId="'dataSuccessOrFailedGraph'"
                  :data="[
                  {
                    value: processedRowNumber - writtenRowNumber,
                    name: '失败行数',
                  },
                  { value: writtenRowNumber, name: '写入成功行数' },
                ]"
                  :title="'数据处理情况'"
              />
            </div>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="180px">
          <template v-slot="scope">
            <el-button
                v-if="scope.row.taskState === 1"
                size="small"
                type="danger"
                @click="handleTerminate(scope.$index, scope.row)"
            >停止</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页区域 -->
      <el-pagination
          style="margin: 15px 0"
          :current-page="queryInfo.currentPage"
          :page-sizes="[10, 25, 50, 100]"
          :page-size="queryInfo.pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total"
          background
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
      >
      </el-pagination>
    </el-card>
  </div>
</template>

<script lang="ts" setup>
import {onMounted, reactive, ref, Ref} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {Search} from "@element-plus/icons-vue";
import {httpGet, httpPost, to} from "@/repository/network/";
import StatItem from "@/components/line-graph/index.vue";

const taskRecordTable = ref();

const queryInfo = reactive({
  keyword: "", // 查询参数
  taskState: "", // 任务状态
  currentPage: 1, // 当前页码
  pageSize: 10, // 每页显示条数
});

const taskRecordList = ref([]);
// 总数据条数
const total = ref(0);

const queryLoading = ref(true);

const dialogVisible = ref(false);

// 任务统计信息
interface Statistic {
  processedRowNumber: number;
  readBufferBlockingTimePer10Kr: number;
  readBytesPerSecond: number;
  readRowsPerSecond: number;
  redRowNumber: number;
  srcRowNumber: number;
  writeBufferBlockingTimePer10Kr: number;
  writeBytesPerSecond: number;
  writeRowsPerSecond: number;
  writtenRowNumber: number;
}

// 任务状态数据字典项
interface TaskStateItem {
  value: number,
  label: string
}

const srcRowNumber = ref(new Array<typeof StatItem>());
const redRowNumber = ref(new Array<typeof StatItem>());
const processedRowNumber = ref(0);
const writtenRowNumber = ref(0);
const readBytesPerSecond = ref(new Array<typeof StatItem>());
const readRowsPerSecond = ref(new Array<typeof StatItem>());
const writeBytesPerSecond = ref(new Array<typeof StatItem>());
const writeRowsPerSecond = ref(new Array<typeof StatItem>());
const writeBufferBlockingTimePer10Kr = ref(new Array<typeof StatItem>());
const readBufferBlockingTimePer10Kr = ref(new Array<typeof StatItem>());
const progressOfData = ref(0);
// 定时刷新统计信息
let timer: NodeJS.Timer | null = null;

let taskStates = ref(new Array<TaskStateItem>());

onMounted(() => {
  getTaskStateList();
  getTaskRecodList();
});

async function getTaskStateList() {
  const {resp, error} = await to(
      httpGet("/admin/task-record/taskStates")
  );
  if (resp?.code != 200) {
    ElMessage.error("加载任务状态数据字典出错");
    return;
  }
  taskStates.value = resp?.data?.data;
}

async function getTaskRecodList() {
  queryLoading.value = true;
  const { resp, error } = await to(
      httpPost("/admin/task-record/paging", queryInfo)
  );
  queryLoading.value = false;
  if (resp?.code != 200) {
    ElMessage.error("加载任务记录列表时出错");
    return;
  }
  taskRecordList.value = resp?.data?.data?.records;
  taskRecordList.value = resp?.data?.data?.records;
  total.value = resp?.data?.data?.total;
}

let collapseAll: boolean = false;
async function exapndRow(row: any, expandRows: any) {
  if (row?.taskState !== 1 && expandRows.length > 0) {
    ElMessage.warning("任务运行已结束，无法查看实时性能信息");
    taskRecordTable.value.toggleRowExpansion(row, false);
    return;
  }
  if (timer) {
    clearInterval(timer);
  }
  if (collapseAll || expandRows.length == 0) {
    clearStatistic();
    return;
  }
  if (expandRows.length > 1) {
    collapseAll = true;
    for (let er of expandRows) {
      if (row?.id !== er?.id) {
        taskRecordTable.value.toggleRowExpansion(er, false);
      }
    }
    clearStatistic();
    collapseAll = false;
    // return;
  }
  dialogVisible.value = false;
  await requestTaskPerformence(row);
  if (timer) {
    clearInterval(timer);
  }
  timer = setInterval(() => {
    requestTaskPerformence(row);
  }, 2000);
}

function clearStatistic() {
  if (timer) {
    clearInterval(timer);
  }
  srcRowNumber.value = [];
  redRowNumber.value = [];
  processedRowNumber.value = 0;
  writtenRowNumber.value = 0;
  readBytesPerSecond.value = [];
  readRowsPerSecond.value = [];
  writeBytesPerSecond.value = [];
  writeRowsPerSecond.value = [];
  writeBufferBlockingTimePer10Kr.value = [];
  readBufferBlockingTimePer10Kr.value = [];
}

let now = new Date();
async function requestTaskPerformence(row: any) {
  const { resp, error } = await to(
      httpGet("admin/task-record/statistic", {
        executorUuid: row?.executorUuid,
        taskId: row?.taskInfo?.id,
      })
  );
  if (resp?.code == 402) {
    progressOfData.value = 100.00;
    ElMessage.error(resp?.msg);
    if (timer) {
      clearInterval(timer);
    }
    return;
  }
  if (resp?.code !== 200) {
    console.log("请求失败", resp || error);
    if (timer) {
      clearInterval(timer);
    }
    return;
  }
  const stat = resp.data.statistic as Statistic;
  if (stat) {
    // 统计百分比
    if (stat.processedRowNumber === 0 || stat.srcRowNumber === 0) {
      progressOfData.value = 0;
    } else {
      progressOfData.value = (stat.processedRowNumber / stat.srcRowNumber) * 100;
    }
    // 记录其他指标
    now = new Date(+now + 1000);
    shiftAndPush(srcRowNumber, {
      name: now.toString(),
      value: [
        [now.getHours(), now.getMinutes(), now.getSeconds()].join(":"),
        stat.srcRowNumber,
      ],
    });
    shiftAndPush(redRowNumber, {
      name: now.toString(),
      value: [
        [now.getHours(), now.getMinutes(), now.getSeconds()].join(":"),
        stat.redRowNumber,
      ],
    });
    shiftAndPush(readBytesPerSecond, {
      name: now.toString(),
      value: [
        [now.getHours(), now.getMinutes(), now.getSeconds()].join(":"),
        toMB(stat.readBytesPerSecond),
      ],
    });
    shiftAndPush(readRowsPerSecond, {
      name: now.toString(),
      value: [
        [now.getHours(), now.getMinutes(), now.getSeconds()].join(":"),
        stat.readRowsPerSecond,
      ],
    });
    shiftAndPush(writeBytesPerSecond, {
      name: now.toString(),
      value: [
        [now.getHours(), now.getMinutes(), now.getSeconds()].join(":"),
        toMB(stat.writeBytesPerSecond),
      ],
    });
    shiftAndPush(writeRowsPerSecond, {
      name: now.toString(),
      value: [
        [now.getHours(), now.getMinutes(), now.getSeconds()].join(":"),
        stat.writeRowsPerSecond,
      ],
    });
    shiftAndPush(writeBufferBlockingTimePer10Kr, {
      name: now.toString(),
      value: [
        [now.getHours(), now.getMinutes(), now.getSeconds()].join(":"),
        nsToMs(stat.writeBufferBlockingTimePer10Kr),
      ],
    });
    shiftAndPush(readBufferBlockingTimePer10Kr, {
      name: now.toString(),
      value: [
        [now.getHours(), now.getMinutes(), now.getSeconds()].join(":"),
        nsToMs(stat.readBufferBlockingTimePer10Kr),
      ],
    });
    processedRowNumber.value = stat.processedRowNumber;
    writtenRowNumber.value = stat.writtenRowNumber;
  }
}

function nsToMs(ns: number) {
  return Number((ns * 0.000001).toFixed(2));
}

function toMB(value: number) {
  return Number((value / 1048576).toFixed(2));
}

function shiftAndPush(list: Ref<Array<any>>, value: any) {
  // const overLimit = list.value.length >= 100;
  // if (overLimit) {
  //   list.value.shift();
  // }
  list.value.push(value);
}

async function handleSearch() {
  if (timer) {
    clearInterval(timer);
  }
  clearStatistic();
  await getTaskRecodList();
}

function handleTerminate(index: number, row: any) {
  ElMessageBox.confirm("确认停止此任务吗?", "警告", {
    distinguishCancelAndClose: true,
    confirmButtonText: "停止",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(async () => {
        await terminateTask(row?.id, row?.executorUuid, row?.taskInfo?.id);
        if (timer) {
          clearInterval(timer);
        }
        getTaskRecodList();
      })
      .catch(() => {});
}

async function terminateTask(
    taskRecordId?: string,
    executorUuid?: string,
    taskId?: string
) {
  if (taskId) {
    queryLoading.value = true;
    const { resp, error } = await to(
        httpGet("/admin/task-info/terminate", {
          taskRecordId,
          executorUuid,
          taskId,
        })
    );
    queryLoading.value = false;
    if (resp?.code !== 200) {
      ElMessage.error(resp?.msg ?? "停止失败");
      return;
    }
    ElMessage.success(resp.msg);
  }
}

function handleDelete(index: number, row: any) {
  ElMessageBox.confirm("将永久删除此项，确定删除?", "警告", {
    distinguishCancelAndClose: true,
    confirmButtonText: "删除",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(async () => {
        const deleteSuccess = await requestDelete(row?.id);
        if (deleteSuccess) {
          await getTaskRecodList();
          ElMessage({
            type: "success",
            message: "删除成功",
          });
        }
      })
      .catch(() => {
        ElMessage({
          type: "info",
          message: "已取消",
        });
      });
}

async function requestDelete(id?: string): Promise<boolean> {
  if (!id) {
    return false;
  }
  const { resp, error } = await to(
      httpGet("/admin/task-record/remove", { id })
  );
  return resp?.code == 200;
}

// 监听 page size 改变的事件
function handleSizeChange(newSize: number) {
  queryInfo.pageSize = newSize;
  getTaskRecodList();
}
// 监听 页码值 改变的事件
function handleCurrentChange(newPage: number) {
  queryInfo.currentPage = newPage;
  getTaskRecodList();
}
</script>

<style scoped>
#monitorContainer {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
}
</style>